package com.generic.mybatis.dal.interceptor.automap.builder;

import com.generic.mybatis.dal.dao.annotation.AutoValue;
import com.generic.mybatis.dal.interceptor.automap.FieldMapper;
import com.generic.mybatis.dal.interceptor.automap.SqlBuilder;
import com.generic.mybatis.dal.interceptor.automap.TableMapper;
import com.generic.mybatis.dal.interceptor.automap.TableMapperHolder;
import com.generic.mybatis.dal.util.Reflections;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.mapping.MappedStatement;

import javax.persistence.GeneratedValue;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.generic.mybatis.dal.interceptor.automap.builder.SqlBuilderUtil.setKeyGenerater;

/**
 * 插入记录
 *
 * @author wuaj
 */
public class InsertBuilder implements SqlBuilder {
    @Override
    public String buildSql(MappedStatement mappedStatement, Class dtoClass, Object dto) throws Exception {
        if (null == dto) {
            throw new RuntimeException("Sorry,I refuse to build sql for a null object!");
        }
        TableMapper tableMapper = TableMapperHolder.getTableMapper(dtoClass);
        Map dtoFieldMap = PropertyUtils.describe(dto);
        SQL sql = new SQL().INSERT_INTO(tableMapper.getTableName());

        boolean allFieldNull = true;

        for (Map.Entry<String, FieldMapper> fieldMapperEntry : tableMapper.getFieldMapperCache().entrySet()) {
            FieldMapper fieldMapper = fieldMapperEntry.getValue();
            String fieldName = fieldMapper.getFieldName();
            Field field = fieldMapper.getField();
            //support autovalue
            if (field.isAnnotationPresent(AutoValue.class)) {
                AutoValue autoValue = field.getAnnotation(AutoValue.class);
                sql.VALUES(fieldMapper.getDbFieldName(), replaceVar(autoValue.value(), dtoFieldMap));
                allFieldNull = false;
            } else {
                if (field.isAnnotationPresent(GeneratedValue.class)) {
                    if (!mappedStatement.getKeyGenerator().getClass().equals("org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator")) {
                        setKeyGenerater(mappedStatement);
                        setKeyProperties(mappedStatement, new String[]{fieldName});
                    }
                    continue;
                }
                Object value = dtoFieldMap.get(fieldName);
                if (value == null) {
                    continue;
                }
                allFieldNull = false;
                sql.VALUES(fieldMapper.getDbFieldName(), String.format("#{%s}", fieldMapper.getFieldName()));
            }
        }

        if (allFieldNull) {
            throw new RuntimeException("Are you joking? Object " + dto.getClass().getName()
                    + "'s all fields are null, how can i build sql for it?!");
        }
        return sql.toString();
    }

    private String replaceVar(String template, Map<String, Object> paramMap) {
        Matcher m = Pattern.compile("\\$\\{\\w+\\}").matcher(template);

        StringBuffer sb = new StringBuffer();

        while (m.find()) {
            String param = m.group(); //${xx}
            Object value = paramMap.get(param.substring(2, param.length() - 1));

            m.appendReplacement(sb, value == null ? "" : value.toString());
        }

        m.appendTail(sb);
        return sb.toString();
    }

    public static void setKeyProperties(MappedStatement mappedStatement, String[] keyProperties) {
        Reflections.setFieldValue(mappedStatement, "keyProperties", keyProperties);
    }
}
